package org.sean.framework.canary.loadbalancer.predicate;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import org.sean.framework.canary.loadbalancer.PredicateKey;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.http.HttpHeaders;

import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;

public class CompositePredicate extends AbstractServerPredicate {

    private AbstractServerPredicate delegate;

    private List<AbstractServerPredicate> fallbacks = Lists.newArrayList();

    private int minimalFilteredServers = 1;

    private float minimalFilteredPercentage = 0;

    @Override
    public boolean apply(@Nullable PredicateKey input) {
        return delegate.apply(input);
    }


    public static class Builder {

        private CompositePredicate toBuild;

        Builder(AbstractServerPredicate primaryPredicate) {
            toBuild = new CompositePredicate();
            toBuild.delegate = primaryPredicate;
        }

        Builder(AbstractServerPredicate... primaryPredicates) {
            toBuild = new CompositePredicate();
            Predicate<PredicateKey> chain = Predicates.<PredicateKey>and(primaryPredicates);
            toBuild.delegate = AbstractServerPredicate.ofKeyPredicate(chain);
        }

        public Builder addFallbackPredicate(AbstractServerPredicate fallback) {
            toBuild.fallbacks.add(fallback);
            return this;
        }

        public Builder setFallbackThresholdAsMinimalFilteredNumberOfServers(int number) {
            toBuild.minimalFilteredServers = number;
            return this;
        }

        public Builder setFallbackThresholdAsMinimalFilteredPercentage(float percent) {
            toBuild.minimalFilteredPercentage = percent;
            return this;
        }

        public CompositePredicate build() {
            return toBuild;
        }
    }

    public static Builder withPredicates(AbstractServerPredicate... primaryPredicates) {
        return new Builder(primaryPredicates);
    }

    public static Builder withPredicate(AbstractServerPredicate primaryPredicate) {
        return new Builder(primaryPredicate);
    }

    /**
     * Get the filtered servers from primary predicate, and if the number of the filtered servers
     * are not enough, trying the fallback predicates
     */
    @Override
    public List<ServiceInstance> getEligibleServers(List<ServiceInstance> servers, HttpHeaders headers) {
        List<ServiceInstance> result = super.getEligibleServers(servers, headers);
        Iterator<AbstractServerPredicate> i = fallbacks.iterator();
        while (!(result.size() >= minimalFilteredServers && result.size() > (int) (servers.size() * minimalFilteredPercentage))
                && i.hasNext()) {
            AbstractServerPredicate predicate = i.next();
            result = predicate.getEligibleServers(servers, headers);
        }
        return result;
    }
}
